package com.hins.component.hlock.core;

import com.hins.component.hlock.annotation.Hlock;
import com.hins.component.hlock.annotation.HlockKey;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Leslie
 * @date 2021/9/7 16:45
 */
public class BusinessKeyProvider {

    private static final Logger logger = LoggerFactory.getLogger(BusinessKeyProvider.class);

    private ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    private ExpressionParser parser = new SpelExpressionParser();

    /**
     * 获取key
     *
     * @param joinPoint 连接点
     * @param hlock     注解
     * @return 根据hlock注解的keys值和HlockKey的值拼接一个key：-userId-1
     */
    public String getKeyName(JoinPoint joinPoint, Hlock hlock) {

        Method method = getMethod(joinPoint);

        List<String> definitionKeys = getSpelDefinitionKey(hlock.keys(), method, joinPoint.getArgs());
        List<String> keyList = new ArrayList<>(definitionKeys);

        List<String> parameterKeys = getParameterKey(method.getParameters(), joinPoint.getArgs());
        keyList.addAll(parameterKeys);

        return StringUtils.collectionToDelimitedString(keyList, "", "-", "");
    }

    /**
     * 获取方法
     *
     * @param joinPoint 连接点
     * @return public java.lang.String org.springframework.boot.autoconfigure.hlock.test.TestService.getValue(java.lang.String,java.lang.Integer) throws java.lang.Exception
     */
    private Method getMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            try {
                method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
            } catch (Exception e) {
                logger.error("error", e);
            }
        }
        return method;
    }

    /**
     * 获取spel值数据列表
     *
     * @param definitionKeys  spel值数值：[#param1, #param2]
     * @param method          public java.lang.String org.springframework.boot.autoconfigure.hlock.test.TestService.getValue(java.lang.String) throws java.lang.Exception
     * @param parameterValues 传入值数组：[1,2]
     * @return [1, 2]
     */
    private List<String> getSpelDefinitionKey(String[] definitionKeys, Method method, Object[] parameterValues) {
        List<String> definitionKeyList = new ArrayList<>();

        for (String definitionKey : definitionKeys) {
            if (!ObjectUtils.isEmpty(definitionKey)) {
                EvaluationContext context = new MethodBasedEvaluationContext(null, method, parameterValues, nameDiscoverer);
                Object objKey = parser.parseExpression(definitionKey).getValue(context);
                definitionKeyList.add(ObjectUtils.nullSafeToString(objKey));
            }
        }
        return definitionKeyList;
    }

    /**
     * 获取注解HlockKey的值
     *
     * @param parameters      [java.lang.String arg0, java.lang.Integer arg1]
     * @param parameterValues [user1, 1]
     * @return 返回注解HlockKey上的value值，没有则取入参值
     */
    private List<String> getParameterKey(Parameter[] parameters, Object[] parameterValues) {
        List<String> parameterKey = new ArrayList<>();
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].getAnnotation(HlockKey.class) != null) {
                HlockKey keyAnnotation = parameters[i].getAnnotation(HlockKey.class);
                if (keyAnnotation.value().isEmpty()) {
                    Object parameterValue = parameterValues[i];
                    parameterKey.add(ObjectUtils.nullSafeToString(parameterValue));
                } else {
                    StandardEvaluationContext context = new StandardEvaluationContext(parameterValues[i]);
                    Object key = parser.parseExpression(keyAnnotation.value()).getValue(context);
                    parameterKey.add(ObjectUtils.nullSafeToString(key));
                }
            }
        }
        return parameterKey;
    }

}
